﻿using System;
using System.Collections.Generic;
using System.Linq;
using Nomo.CoreModule;
using UnityEngine;

namespace Nomo.UnityCoreModule
{
    /// <summary>
    /// 音效管理器。
    /// </summary>
    public sealed class SoundManager : SingletonBehaviour<SoundManager>
    {
        private          float              _volumeScale   = 1F;
        private readonly List<SoundHandler> _soundHandlers = new List<SoundHandler>();

        /// <summary>
        /// 音量因数改变时。
        /// </summary>
        public event Action VolumeScaleChanged;

        /// <summary>
        /// 音量因数。
        /// </summary>
        public float VolumeScale
        {
            get => _volumeScale;
            set
            {
                value = Mathf.Clamp01(value);

                if (Mathf.Approximately(value, _volumeScale))
                {
                    return;
                }

                _volumeScale = value;
                VolumeScaleChanged?.Invoke();
            }
        }

        /// <summary>
        /// 播放音效。
        /// </summary>
        /// <param name="key">音效名。</param>
        public void Play(string key)
        {
            Play(key, 1F);
        }

        /// <summary>
        /// 播放音效。
        /// </summary>
        /// <param name="key">音效名。</param>
        /// <param name="volume">音量。</param>
        public void Play(string key, float volume)
        {
            GetSoundHandler().Play(key, volume);
        }

        private SoundHandler GetSoundHandler()
        {
            try
            {
                return _soundHandlers.First(soundHandler => !soundHandler.IsPlaying);
            }
            catch
            {
                return NewSoundHandler();
            }
        }

        private SoundHandler NewSoundHandler()
        {
            var instance = new GameObject(nameof(SoundHandler));
            instance.transform.SetParent(transform);
            var soundHandler = instance.AddComponent<SoundHandler>();
            _soundHandlers.Add(soundHandler);
            return soundHandler;
        }

        /// <summary>
        /// 停止第一个满足 <paramref name="predicate"/> 的音效。
        /// </summary>
        /// <param name="predicate">条件。</param>
        public void StopFirst(Predicate<SoundHandler> predicate)
        {
            StopFirst(predicate, false);
        }

        /// <summary>
        /// 停止第一个满足 <paramref name="predicate"/> 的音效，并根据 <paramref name="destroy"/> 指定是否要摧毁其所在的容器。
        /// </summary>
        /// <param name="predicate">条件。</param>
        /// <param name="destroy">是否将其所在的容器摧毁以便释放内存？</param>
        public void StopFirst(Predicate<SoundHandler> predicate, bool destroy)
        {
            if (predicate == null)
            {
                return;
            }

            try
            {
                var firstSoundHandler = _soundHandlers.First(soundHandler => predicate(soundHandler));
                firstSoundHandler.Stop();

                if (!destroy)
                {
                    return;
                }

                _soundHandlers.Remove(firstSoundHandler);
                _soundHandlers.TrimExcess();
                Destroy(firstSoundHandler.gameObject);
            }
            catch
            {
                return;
            }

            Log.Info("清理了 1 个未被使用的音效处理者。");
        }

        /// <summary>
        /// 停止所有满足 <paramref name="predicate"/> 的音效。
        /// </summary>
        /// <param name="predicate">条件。</param>
        public void StopAll(Predicate<SoundHandler> predicate)
        {
            StopAll(predicate, false);
        }

        /// <summary>
        /// 停止所有满足 <paramref name="predicate"/> 的音效，并根据 <paramref name="destroy"/> 指定是否要摧毁其所在的容器。
        /// </summary>
        /// <param name="predicate">条件。</param>
        /// <param name="destroy">是否将其所在的容器摧毁以便释放内存？</param>
        public void StopAll(Predicate<SoundHandler> predicate, bool destroy)
        {
            if (predicate == null)
            {
                return;
            }

            var soundHandlers = _soundHandlers.Where(soundHandler => predicate(soundHandler)).ToList();
            var count         = soundHandlers.Count;
            if (count == 0)
            {
                return;
            }
            for (var i = 0; i < count; i++)
            {
                var soundHandler = soundHandlers[i];
                soundHandler.Stop();
                if (destroy)
                {
                    _soundHandlers.Remove(soundHandler);
                    Destroy(soundHandler.gameObject);
                }
            }

            if (destroy)
            {
                _soundHandlers.TrimExcess();
            }

            Log.Info($"清理了 {count} 个未被使用的音效处理者。");
        }
    }
}
